#include <math.h>
#include <stdlib.h>
#include <unistd.h>

#include <iostream>

#include <mpi.h>

#include "gsolverBBPv3.h"

/**
 * A BBP solver : starts with jobs with last due dates
 *
 * Dans cette version 3 de la BBP, on demarre avec les jobs reordonnes dans le sens des dates dues croissantes. Mais
 * contrairement a l'exploration brutale de la version 2, cette fois on ne considere pas n'importe quel numero de batch.
 * Pour chaque job, on choisit de le rajouter soit dans un batch deja existant dont la capacite residuel est suffisante
 * pour stocker le nouveau job, soit dans un nouveau batch.
 *
 * Le nouveau batch est ordonnance avant les autres batchs deja crees.
 *
 * ATTENTION : Pour que cette solution soit optimale, sachant que l'on
 * part d'un probleme pour lequel les jobs sont classes par date due croissante, il est egalement necessaire que le cout de stockage
 * de tous les jobs soient identiques. Dans le cas contraire, la solution ainsi construite n'est plus certaine d'etre optimale.
 */
GSolverBBPv3::GSolverBBPv3(GSupplyLinkProblem& pb, int node_, int p_):problem(pb),bestSolution(pb),bestSolutionDueDateOrdered(pb),currentSolutionDueDateOrdered(pb),node(node_),nbrNodes(p_) {
}




/**
 *
*/
void GSolverBBPv3::initBestSolutionWithFFD() {
    int nbrbatch=0 ;
    vector<double> tabRemainingCapacity ;

    for (int job=problem.getNP()-1;job>=0;job--) {
        // Find a suitable batch
        int batchjob = nbrbatch-1 ;
        bool found = false ;
        double jobsize = problem.getJobSize(job) ;
        while (!found && batchjob>=0) {
            if (tabRemainingCapacity[batchjob]>=jobsize) {
                found = true ;
            }
            else
                batchjob-- ;
        }

        if (!found) { // Create a new batch
            tabRemainingCapacity.insert(tabRemainingCapacity.begin(),problem.getTransporter(0).getCapacity()) ;
            for (int job2=job+1;job2<problem.getNP();++job2)
                bestSolutionDueDateOrdered.setBatchIndice(job2, bestSolutionDueDateOrdered.getBatchIndice(job2)+1) ;
            batchjob = 0 ;
            nbrbatch++ ;
        }
        bestSolutionDueDateOrdered.setBatchIndice(job, batchjob+1) ;
        bestSolutionDueDateOrdered.setDeliveryCompletionTime(job, (double)batchjob+1) ;
        tabRemainingCapacity[batchjob] -= jobsize ;
    }

    bestSolutionDueDateOrdered.evaluate() ;
    if (!node) {
      cout << "FFD evaluation = eval="<< bestSolutionDueDateOrdered.getEvaluation() ;
      cout << "(transp=" << bestSolutionDueDateOrdered.getEvaluationTransporter() ;
      cout << " ; stock=" << bestSolutionDueDateOrdered.getEvaluationCustomer() <<" )" << endl;
    }
}

/**
 *
 */
void GSolverBBPv3::solve() {
    solveInit() ;
    solveMain() ;
    solveEnd() ;
}


/**
 *
 */
void GSolverBBPv3::solveEnd() {
  if (!node) {
    // Transform "bestSolutionDueDateOrdered" to "bestSolution"
    bestSolution = this->problem.getBestSolutionFromBestSolutionDueDateOrdered(bestSolutionDueDateOrdered) ;
    cout << "==================================================" << endl ;
    cout << "End of MASTER BBP : besteval=" << bestSolution.getEvaluation() << endl ;
    cout << "==================================================" << endl ;
  }

  cpuComputingUsage = 100.0*(double)(cpuComputing.getTotalTime() )/(double)(cpuTotal.getTotalTime()) ;
  cpuMPICommUsage = 100.0*(double)(cpuMPIComm.getTotalTime() )/(double)(cpuTotal.getTotalTime()) ;
  //cpuWaitingUsage = 100.0*(double)(cpuWaiting.getTotalTime() )/(double)(cpuTotal.getTotalTime()) ;

  if (node) coutline() ;
  cout << "Node " << node << " : CPU computing usage =" << cpuComputingUsage << "%" << endl ;
  cout << "Node " << node << " : CPU MPI Comm  usage =" << cpuMPICommUsage << "%" << endl ;
}


